package com.lxc.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lxc.entity.BusinessException;
import com.lxc.entity.RefreshToken;
import com.lxc.entity.User;
import com.lxc.mapper.RefreshTokenMapper;
import com.lxc.mapper.UserMapper;
import com.lxc.service.UserService;
import com.lxc.util.MD5;
import com.lxc.util.RSA;
import com.lxc.util.Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RefreshTokenMapper refreshTokenMapper;
    @Override
    @Transactional
    /*事务检查*/
    public void register(User user) {
//        检查手机号，密码是否为空
        if (user.getPhone() == null || user.getPassword() == null) {
            throw new BusinessException(400, "手机号或密码不能为空");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", user.getPhone());
        User u = userMapper.selectOne(queryWrapper);
        if (u != null){
            throw new BusinessException(400, "手机号已被注册");
        }

//        获取当前时间
        user.setCreatedAt(new java.sql.Timestamp(System.currentTimeMillis()));

//        私钥解密
        String password=user.getPassword();
        /*TODO模拟前端获取公钥后加密*/
        try {
            password=RSA.encrypt(password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        String rsaPassword=null;
        try {
            rsaPassword= RSA.decrypt(password);
        } catch (Exception e){
            throw new BusinessException(500, "密码解密失败，请重试");
        }
//        随机生成盐值
        String salt=String.valueOf(Math.random());
        user.setSalt(salt);
        try {
            user.setPassword(MD5.md5EncryptWithSalt(rsaPassword, salt));
        } catch (Exception e) {
            // 可记录日志
            throw new BusinessException(500, "密码加密失败，请重试");
        }

        userMapper.insert(user);
    }

    /*用这个方法生成时间一长一短的两个令牌返回给前端，同时将长时间的token存到数据库中。短的令牌accessToken过期的时候，
    前端可以再次通过长时间的令牌refreshToken访问后端来刷新短时间的令牌。
     */

    @Override
    @Transactional
    /*双令牌登录校验*/
    public Map<String,Object> login(User user) {
//        检查是否为空
        if (user.getPhone() == null || user.getPassword() == null) {
            throw new BusinessException(400, "手机号或密码不能为空");
        }
//        用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", user.getPhone());
        User u = userMapper.selectOne(queryWrapper);
        if (u == null){
            throw new BusinessException(400, "用户不存在");
        }
//        获取密码解密
        String password=user.getPassword();
        /*TODO模拟前端获取公钥后加密*/
        try {
            password=RSA.encrypt(password);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        String rsaPassword=null;
        try {
            rsaPassword= RSA.decrypt(password);
        } catch (Exception e){
            throw new BusinessException(500, "密码解密失败，请重试");
        }
//        MD5加密
        String md5Password=null;
        try {
            md5Password= MD5.md5EncryptWithSalt(rsaPassword, u.getSalt());
        } catch (Exception e) {
            // 可记录日志
            throw new BusinessException(500, "MD5加密失败，请重试");
        }

//        校验账号密码
        if (!md5Password.equals(u.getPassword())){
            throw new BusinessException(400, "账号或密码错误");
        }

//        颁发携带userid的token给前端
        long userId=u.getId();
        String accessToken = null;
        String refreshToken = null;
        try {
            accessToken= Token.generateToken(userId);
            refreshToken = Token.generateRefreshToken(userId);
        } catch (Exception e){
            throw new BusinessException(500, "token生成失败，请重试");
        }
        
        // 保存refresh到数据库
        QueryWrapper<RefreshToken> queryWra = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        refreshTokenMapper.delete(queryWra);
        RefreshToken rf = new RefreshToken();
        rf.setUserId((int) userId);
//        rf.setRefreshToken(String.valueOf(refreshToken));
        // 确保refreshToken长度不超过数据库字段限制（255）
        int maxAllowedLength = 255;
        String tokenToSave = refreshToken;
        if (refreshToken.length() > maxAllowedLength) {
            tokenToSave = refreshToken.substring(0, maxAllowedLength);
        }
        rf.setRefreshToken(tokenToSave);
//        refreshToken.setExpiresAt();
        refreshTokenMapper.insert(rf);

        // 将双token返回给前端
        Map<String, Object> result = new java.util.HashMap<>();
        result.put("accessToken", accessToken);
        result.put("refreshToken", refreshToken);
        return result;


    }

    /*同时也可以在刷新短期令牌的时候对refreshToken进行一个刷新，此时需要给前台发短期令牌的同时也发一个长期令牌，
    而且此时也必须要对数据库的长期令牌进行更新！！！*/
    @Override
    public String refreshAccessToken(String refreshToken) throws Exception {
        int maxAllowedLength = 255;
        String tokenToSave = refreshToken;
        if (refreshToken.length() > maxAllowedLength) {
            tokenToSave = refreshToken.substring(0, maxAllowedLength);
        }
        //  从数据库中查询refreshToken
        QueryWrapper<RefreshToken> QueryWrapper = new QueryWrapper<>();
        QueryWrapper.eq("refresh_token", tokenToSave);
        RefreshToken refreshTokenDetail = refreshTokenMapper.selectOne(QueryWrapper);
        if (refreshTokenDetail == null) {
            throw new BusinessException(400, "refreshToken不存在");
        }

        // 没过期则生成一个新的token返回给前端
        Long userId = Long.valueOf(refreshTokenDetail.getUserId());
        String accessToken = Token.generateToken(userId);

        return accessToken;
    }

    @Override
    public void logout(String refreshToken) {

        Long userId = Token.verifyToken(refreshToken);
        QueryWrapper<RefreshToken> qw = new QueryWrapper<>();
        qw.eq("user_id", userId);
        refreshTokenMapper.delete(qw);
    }
}
